Jellyfin Year Review touch = null} />
{#if user}
JJellyfin Year ReviewA YEAR WORTH REPLAYING {#if user} {/if}
{/if}
{#if user} {#if notice}

{notice}

{/if} {#if error}{/if} {/if} {#if !user} {:else if loading}

Setting the scene…

Loading your private review

{:else if !years.length}

NOT QUITE SHOWTIME

Your story is on its way.

No review has been generated for your account. Ask your server operator to generate a year.

{:else if review}
{review.year} / {user.name}

CHAPTER {String(slide + 1).padStart(2, '0')} / {cards[slide].name.toUpperCase()}

{#if card === 0} {review.year}

Your viewing recap

{user.name}, {review.records ? 'see the movies, TV shows, and viewing habits that made up your year.' : 'no viewing activity was recorded for this year.'}

{:else if card === 1}

Your watch time

{hours(review.seconds)}hours watched{review.availability.exactWatchTime.available ? '' : ' · end-attributed estimate'}
{duration(review.movieSeconds)}Movies
{duration(review.tvSeconds)}Television
{count(review.records)}Activity records
{count(review.uniqueTitles)}Unique movies & episodes
{:else if card === 2 || card === 3} {@const items = card === 2 ? review.movies : review.shows}

{card === 2 ? 'Movies watched' : 'TV shows watched'}

Everything you watched in {review.year}, grouped by month.

{:else if card === 4}

Unfinished movies

Movies where you watched less than {review.rules.completionPercent}% of the runtime.

{#if review.abandoned.length}
    {#each review.abandoned.slice(0, 5) as item}
  1. {item.title}

    {Math.round(item.completionPercent)}% of the movie watched

  2. {/each}
{:else}

{review.availability.completion.available ? 'No unfinished movies found in supported records.' : 'Completion is unavailable without runtime and actual position ranges.'}

{/if} {:else if card === 5}

Your top genres

Genres ranked by watch time.

{#if review.genres.length} v.seconds / 3600)} labels={review.genres.slice(0, 7).map(v => v.name)} metric="h" />{:else}

No genre history is available.

{/if} {:else if card === 6}

Viewing by hour

Your viewing activity across the hours of the day.

`${String(i).padStart(2, '0')}:00`)} metric={review.distributionMetric === 'watched minutes' ? 'min' : 'records'} />
{:else if card === 7}

Viewing by weekday

Your viewing activity for each day of the week.

{:else if card === 8}

Viewing by month

Your viewing activity for each month of {review.year}.

{:else if card === 9}

Your devices and apps

Your most-used devices and apps, ranked by watch time.

Devices

v.seconds / 3600)} labels={review.devices.slice(0, 5).map(v => v.name)} metric="h" />

Apps & clients

v.seconds / 3600)} labels={review.clients.slice(0, 5).map(v => v.name)} metric="h" />
{:else if card === 10}

Your longest viewing session

{#if review.marathon}
{hours(review.marathon.watchedSeconds)}hours watched in your longest session

{duration(review.marathon.elapsedSeconds)} elapsed · {review.marathon.records} records

{new Date(review.marathon.start).toLocaleString(undefined, { timeZone: review.timezone })}

{:else}

{review.availability.marathon.reason}

{/if} {:else if card === 11}

Your viewing profile

{#if review.personality}

{review.personality.name}

{review.personality.explanation}

{:else}

{review.availability.personality.reason}

{/if} {:else if card === 12}

Your watch-time ranking

Your total watch time compared with other eligible viewers.

{#if review.ranking}
#{review.ranking.rank}of {review.ranking.cohort} eligible viewers
{Math.round(review.ranking.percentile)}%Viewers with less watch time
{duration(review.seconds)}Your total hours watched · movies & TV shows
{:else}

{review.availability.ranking.reason}

{/if} {:else}

SUMMARY / {review.year}

Your year in summary

{#if review.availability.exactWatchTime.available !== false}
{duration(review.seconds)}Your watch time
{/if}{#if review.availability.personality.available !== false}
{review.personality?.name || 'No viewing profile'}Your viewing profile
{/if}

You have reached the end of your viewing recap.

{/if}
{String(slide + 1).padStart(2, '0')} / {cards.length}
Behind your review · source & availability

{review.source}

{review.coverage}

Snapshot: {new Date(review.generatedAt).toISOString()} · Format v{review.schemaVersion}

{#each review.warnings as warning}

{warning}

{/each}
{:else}

Let’s try that again.

{/if}
{#if user}{/if}